const path = require('path')
const types = require('@babel/types')
const generator = require('@babel/generator').default
const traverse = require('@babel/traverse').default

class NormalModule {
  constructor(data) {
    this.context = data.context
    this.name = data.name
    // this.entry = data.entry
    this.rawRequest = data.rawRequest
    this.moduleId = data.moduleId
    this.parser = data.parser
    this.resource = data.resource
    this._source
    this._ast
    this.dependencies = []
  }

  build(compilation, callback) {
    // 01 从文件中读取到将来需要被加载的module内容
    // 02 如果不是js模块需要loader进行处理，最终返回js模块
    // 03 将js代码转为ast语法树
    // 04 引用很多模块，因此需要递归完成
    // 05 需要重复执行
    this.doBuild(compilation, (err) => {
      this._ast = this.parser.parse(this._source)// _ast为当前module的语法树

      // 利用ast做了代码修改
      traverse(this._ast, {
        CallExpression: (nodePath) => {
          let node = nodePath.node
          if (node.callee.name === 'require') {
            let modulePath = node.arguments[0].value //获取原始请求路径 ./title.js

            let moduleName = modulePath.split(path.posix.sep).pop() //获取被加载模块名称 title.js

            let extName = moduleName.indexOf('.') == -1 ? '.js' : '' //只处理js文件
            moduleName += extName
            let depResource = path.posix.join(path.posix.dirname(this.resource), moduleName)
            let depModuleId = './' + path.posix.relative(this.context, depResource) //./src/title.js

            // 记录当前被依赖模块的信息
            this.dependencies.push({
              name: this.name,
              context: this.context,
              rawRequest: moduleName,
              moduleId: depModuleId,
              resource: depResource
            })

            node.callee.name = '__webpack_require__'
            node.arguments = [types.stringLiteral(depModuleId)]
          }
        }
      })

      // 把ast转回code
      let { code } = generator(this._ast)

      this._source = code

      callback(err)
    })
  }

  doBuild(compilation, callback) {
    this.getSource(compilation, (err, source) => {
      this._source = source
      callback()
    })
  }

  getSource(compilation, callback) {
    compilation.inputFileSystem.readFile(this.resource, 'utf8', callback)
  }
}

module.exports = NormalModule